﻿using System;
using System.Linq;

namespace Innovative.ProXr
{
	/// <summary>
	/// Represents a single one byte value that can be used for
	/// sending commands to the device or for receiving data
	/// from the device.
	/// </summary>
	public class RelayCommand
	{
		private int _ordinal = 0;
		private RelayByte _value = 0;
		private RelayByte[] _allowedValues = new RelayByte[1] { 0 };
		private bool _isRequired = false;
		private bool _isReadOnly = false;
		private string _name = string.Empty;
		private string _desription = string.Empty;

		/// <summary>
		/// Creates a new instance of RelayByte with a 
		/// value of 0.
		/// </summary>
		public RelayCommand()
		{
		}

		/// <summary>
		/// Specifies the order in which this command appears in the list. This is
		/// used when sending the commands to the relay control. The commands are sent
		/// in order with the smallest number first and the largest number last.
		/// </summary>
		public int Ordinal
		{
			get
			{
				return _ordinal;
			}
			set
			{
				_ordinal = value;
			}
		}

		/// <summary>
		/// The byte value of this command
		/// </summary>
		public RelayByte Value
		{
			get
			{
				return _value;
			}
			set
			{
				if (!this.IsReadOnly)
				{
					_value = value;

					if (value != null)
					{
						if (!this.VerifyValue(_value))
						{
							throw new InvalidCommandValueException();
						}
					}
					else
					{
						throw new ArgumentNullException();
					}
				}
				else
				{
					throw new ReadOnlyCommandException();
				}
			}
		}

		/// <summary>
		/// Get or sets value indicating if this byte
		/// is required when combined with others in a command set.
		/// </summary>
		public bool IsRequired
		{
			get
			{
				return _isRequired;
			}
			set
			{
				_isRequired = value;
			}
		}

		/// <summary>
		/// Indicates whether or not the value of this command can be modified after it has been created.
		/// A value of True prevents the value from be changed.
		/// </summary>
		public bool IsReadOnly
		{
			get
			{
				return _isReadOnly;
			}
			set
			{
				_isReadOnly = value;
			}
		}

		/// <summary>
		/// The name of this command used to distinguish it in the command set and provide
		/// information guiding the setting of optional values.
		/// </summary>
		public string Name
		{
			get
			{
				return _name;
			}
			set
			{
				_name = value;
			}
		}

		/// <summary>
		/// A detailed description of this command and the effect it has on the command set.
		/// </summary>
		public string Description
		{
			get
			{
				return _desription;
			}
			set
			{
				_desription = value;
			}
		}

		/// <summary>
		/// Contains an array of bytes values that can be 
		/// used for the Command property.
		/// </summary>
		public RelayByte[] AllowedValues
		{
			get
			{
				return _allowedValues;
			}
			set
			{
				_allowedValues = value;
			}
		}

		/// <summary>
		/// Serves as a hash function for a particular type.
		/// </summary>
		/// <returns>A hash code for the current RelayByte.</returns>
		public override int GetHashCode()
		{
			return this.Value.GetHashCode();
		}

		/// <summary>
		/// Returns a string that represents the current object.
		/// </summary>
		/// <returns>A string that represents the current object.</returns>
		public override string ToString()
		{
			return string.Format("{0}", this.Value);
		}

		/// <summary>
		/// Creates a read-only command from a value.
		/// </summary>
		/// <param name="ordinal">The order in the command set this command will appear.</param>
		/// /// <param name="value">The value that this command will have when created.</param>
		/// <param name="name">The name of this command.</param>
		/// <param name="description">A detailed description of this command.</param>
		/// <returns>A newly created instance of a RelayCommand.</returns>
		public static RelayCommand CreateReadOnlyCommand(int ordinal, RelayByte value, string name = "", string description = "")
		{
			return new RelayCommand()
				{
					Ordinal = ordinal,
					AllowedValues = new RelayByte[] { value },
					Value = value,
					IsRequired = true,
					IsReadOnly = true,
					Name = name,
					Description = description
				};
		}

		/// <summary>
		/// Creates a optional parameter command from a value.
		/// </summary>
		/// <param name="name">The name of this command.</param>
		/// <param name="description">A detailed description of this command.</param>
		/// <returns>A newly created instance of a RelayCommand.</returns>
		public static RelayCommand CreateOptionalParameterCommand(int ordinal, RelayByte[] allowedValues, string name = "", string description = "")
		{
			return new RelayCommand()
			{
				Ordinal = ordinal,
				AllowedValues = allowedValues,
				IsRequired = false,
				IsReadOnly = false,
				Name = name,
				Description = description
			};
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="ordinal"></param>
		/// <param name="allowedValues"></param>
		/// <param name="name"></param>
		/// <param name="description"></param>
		/// <returns></returns>
		public static RelayCommand CreateNonOptionalParameterCommand(int ordinal, RelayByte[] allowedValues, string name = "", string description = "")
		{
			return new RelayCommand()
			{
				Ordinal = ordinal,
				AllowedValues = allowedValues,
				IsRequired = true,
				IsReadOnly = false,
				Name = name,
				Description = description
			};
		}

		/// <summary>
		/// Creates the read-only command with a value of 254 used in all command sets.
		/// </summary>
		public static RelayCommand Command254
		{
			get
			{
				return RelayCommand.CreateReadOnlyCommand(0, 254, "CommandMode", "Enter the device into Command Mode.");
			}
		}

		internal bool VerifyValue(RelayByte value)
		{
			bool returnValue = false;

			var qry = from tbl in this.AllowedValues
					  where tbl!=null && tbl.Value==value
					  select tbl;

			if (qry.Count() > 0)
			{
				returnValue = true;
			}

			return returnValue;
		}

		internal bool VerifyValue()
		{
			bool returnValue = false;

			if (this.AllowedValues.Contains(this.Value))
			{
				returnValue = true;
			}

			return returnValue;
		}
	}
}
